/*
 * case_1D1t_GA_m0.h
 *
 *  Created on: 12-09-2012
 *      Author: mith
 */

#ifndef CASE_H_1D1t_GA_m0
#define CASE_H_1D1t_GA_m0

// BASIC LIBLARIES
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<math.h>
#include<time.h>
// MY LIBRARIES
#include "mros-v42.h"
#include "rootGSL.h"

namespace case_1D1t_GA_m0
{

const unsigned N_PAR = 5;
const unsigned N_PAR_INT = 1;
const unsigned N_VAR = 4;
const char OUTPUT_FILE[] = "test.dat";
const char OUTPUT_FILE_GNUPLOT[] = "test.gnu";

class Par : public Parameters {
	private:
		int NtabD; // counter for tabD double table
		int NtabI; // counter for tabI integer table

		double *tabD;
		int *tabI;

		enum labelD {_sdelta, _J, _t, _U};
		enum labelI {_L};

	public:

		Par() : NtabD(-1), NtabI(-1) {}
		Par(unsigned n) : NtabD((int)n - N_PAR_INT), NtabI(N_PAR_INT), tabD(new double[NtabD]), tabI(new int[NtabI]) {
			zeroAll();
		}
		~Par(){
			if(NtabD != -1) { delete[] tabD; NtabI = -1; }
			if(NtabI != -1) { delete[] tabI; NtabD = -1; }
		}
		Par(Par& other) : NtabD(other.NtabD), NtabI(other.NtabI) {
			if(NtabD > 0) {
				tabD = new double[NtabD];
				for(int i=0; i<NtabD; i++) { tabD[i] = other.tabD[i]; }
			}
			if(NtabI > 0) {
				tabI = new int[NtabI];
				for(int i=0; i<NtabI; i++) { tabI[i] = other.tabI[i]; }
			}
		}

		void setNoOfDoubles(unsigned n) { if(NtabD > 0) { releaseDoubles(); } NtabD = (int)n; if(n > 0 ) { tabD = new double[n]; } }
		void setNoOfInts(unsigned n) { if(NtabI != -1) { releaseInts(); } NtabI = (int)n; if(n > 0 ) { tabI = new int[n]; } }
		void releaseDoubles() { if(NtabD != -1) { delete[] tabD; NtabD = -1; } }
		void releaseInts() { if(NtabI != -1) { delete[] tabI; NtabI = -1; } }

		double sdelta() const { return tabD[_sdelta]; };
		double J() const { return tabD[_J]; };
		double t() const { return tabD[_t]; };
		double U() const { return tabD[_U]; };
		int L() const { return tabI[_L]; };

		int getNtabD() { return NtabD; }
		int getNtabI() { return NtabI; }

		void set_sdelta(double d) { tabD[_sdelta]=d; };
		void set_J(double d) { tabD[_J]=d; };
		void set_t(double d) { tabD[_t]=d; };
		void set_U(double d) { tabD[_U]=d; };
		void set_L(int n) { tabI[_L]=n; };

		void print(FILE *file=stdout) const;
		void print_verboten(FILE *file=stdout) const;
		void print_label(FILE *file=stdout) const;

		void zeroAll() {
			for(int i=0; i<NtabD; i++) { tabD[i] = 0.; }
			for(int i=0; i<NtabI; i++) { tabI[i] = 0; }
		}


};

class Var : public Variables {

	private:
		int NtabD; // counter for tabD double table

		double *tabD;

		enum labelD {_BDelta, _chi, _d, _mu};

	public:

		Var() : NtabD(-1) {}
		Var(unsigned n) : NtabD((int)n) { tabD = new double[n]; zeroAll(); }
		~Var() { if(NtabD != -1) { delete[] tabD; NtabD = -1; } }
		Var(Var& other) : NtabD(other.NtabD) {
			if(NtabD > 0) {
				tabD = new double[NtabD];
				for(int i=0; i<NtabD; i++) { tabD[i] = other.tabD[i]; }
			}
		}
		double &operator[](int i) {
			if(i < NtabD && i >= 0) return tabD[i];
			else exit(EXIT_FAILURE);
		}
		const double &operator[](int i) const {
			if(i < NtabD && i >= 0) return tabD[i];
			else exit(EXIT_FAILURE);
		}
		Var &operator=(const Var &rhs) {
			releaseDoubles();
			NtabD = rhs.getNtabD();
			if(NtabD > 0) {
				tabD = new double[NtabD];
				for(int i=0; i<NtabD; i++) { tabD[i] = rhs.tabD[i]; }
			}
			return *this;
		  }

		void setNoOfDoubles(unsigned n) { NtabD = (int)n; if(n > 0 ) { tabD = new double[n]; } }
		void releaseDoubles() { if(NtabD != -1) { delete[] tabD; NtabD = -1; } }

		double BDelta() const { return tabD[_BDelta]; }
		double chi() const { return tabD[_chi]; }
		double mu() const { return tabD[_mu]; }
		double d() const { return tabD[_d]; }

		//int getNtabD() { return NtabD; }
		int getNtabD() const { return NtabD; }

		void set_BDelta(double d) { tabD[_BDelta] = d; }
		void set_chi(double d) { tabD[_chi] = d; }
		void set_mu(double d) { tabD[_mu] = d; }
		void set_d(double d) { tabD[_d] = d; }
		void set(Variables *V) {
			for(int i=0; i<NtabD; i++) { tabD[i] = (*V)[i]; }
		}

		void shake(double d=1e-6) {
			for(int i=0; i<NtabD; i++) { tabD[i] += (2.*frand()-1.)*d; }
		}

		void print(FILE *file=stdout) const;
		void print_verboten(FILE *file=stdout) const;
		void print_label(FILE *file=stdout) const;

		void zeroAll() {
			for(int i=0; i<NtabD; i++) { tabD[i] = 0.; }
		}
};

typedef struct Global {
	double Energy;
	double gt;
	double gs;
	double BDelta_sc;
	double m_AF;
	double BDelta_d;
} global;

class Res : public Result {
	private:
		int n;
		Variables *V;
		global glob;

	public:
		Res(int n) : n(n) { if(n>0) { V = new Var(n); } else { exit(EXIT_FAILURE); } }
		~Res() { delete V; }

		void set(gsl_multiroot_fsolver *s);

		void print(FILE *file=stdout) const;
		void print_verboten(FILE *file=stdout) const;
		void print_label(FILE *file=stdout) const;

		Var* get_V(){ return (Var*)V; }
};

// FUNKCTION (only declaration)
int function(const gsl_vector * x, void *params, gsl_vector * f);

void example_inf();
void example_1D();
void run_1D_n();

} // end of namespace case_1D1t_GA_m0
#endif /* CASE_H_1D1t_GA_m0 */
